﻿#region Copyright (c) 10/02/2012, Olaf Kober <amarok.projects@gmail.com>
//================================================================================
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//	copies of the Software, and to permit persons to whom the Software is
//	furnished to do so, subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in
//	all copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//	THE SOFTWARE.
//================================================================================
#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Threading.Tasks;
using Amarok.Agents;


namespace Amarok.Presentation
{
	/// <summary>
	/// This type represents a base class for controller implementations. Basically, a controller is an agent 
	/// with additional presentation-specific functionality.
	/// </summary>
	public abstract class Controller : Agent,
		IController
	{
		// static data
		private static IControllerFactory sFactory = new DefaultControllerFactory();

		// data
		private readonly List<IDisposable> mCommands = new List<IDisposable>();

		// references
		private readonly IView mView;


		#region ++ Public Static Interface ++

		/// <summary>
		/// Gets a reference to the controller factory.
		/// </summary>
		public new static IControllerFactory Factory
		{
			get
			{
				return sFactory;
			}
		}


		/// <summary>
		/// Replaces the default controller factory by the factory supplied to the method.
		/// </summary>
		/// 
		/// <param name="factory">
		/// The new controller factory to use.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		public static void SetFactory(IControllerFactory factory)
		{
			Verify.NotNull(factory, "factory");
			sFactory = factory;
		}

		#endregion

		#region ++ IController Interface ++

		/// <summary>
		/// Gets a reference to the view associated with the controller.
		/// </summary>
		IView IController.View
		{
			get
			{
				return mView;
			}
		}

		#endregion

		#region ## Protected Interface ##

		/// <summary>
		/// Initializes a new instance.
		/// </summary>
		/// 
		/// <param name="environment">
		/// The agent environment for this agent.</param>
		/// <param name="options">
		/// The agent options for this agent.</param>
		/// <param name="view">
		/// The view associated with this controller.</param>
		/// <param name="viewModel">
		/// The view model that is used as intermediate between controller and view.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected Controller(AgentEnvironment environment, AgentOptions options, IView view, Object viewModel)
			: base(environment, options)
		{
			Verify.NotNull(view, "view");
			Verify.NotNull(viewModel, "viewModel");

			mView = view;
			OnPresetViewModel(viewModel);
			mView.ViewModel = viewModel;
		}

		#endregion

		#region ## Protected Interface (Send) ##

		/// <summary>
		/// Sends the specified view action synchronously to the thread that the view, which is associated 
		/// with this controller, was created on.
		/// </summary>
		/// 
		/// <param name="viewAction">
		/// The view action to send to the view associated with this controller.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected void Send(ViewAction viewAction)
		{
			Verify.NotNull(viewAction, "viewAction");
			mView.Send(viewAction);
		}

		#endregion

		#region ## Protected Interface (SendAsync) ##

		/// <summary>
		/// Sends the specified view action asynchronously to the thread that the view, which is associated 
		/// with this controller, was created on.
		/// </summary>
		/// 
		/// <param name="viewAction">
		/// The view action to send to the view associated with this controller.</param>
		/// 
		/// <returns>
		/// A task that represents the asynchronously running view action. This task can be used to wait for 
		/// completion, to register a continuation and to access the state of the view action execution.</returns>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected Task SendAsync(ViewAction viewAction)
		{
			Verify.NotNull(viewAction, "viewAction");
			return mView.SendAsync(viewAction);
		}

		#endregion

		#region ## Protected Interface (AccessView) ##

		/// <summary>
		/// Gives the supplied action access to the view. The supplied action is synchronously executed in the 
		/// thread that created the associated view. The method blocks until the action returns.
		/// </summary>
		/// 
		/// <param name="action">
		/// The action that gains access to the view.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected void AccessView(Action<IView> action)
		{
			Verify.NotNull(action, "action");

			mView.Invoke(() =>
				{
					// call callback
					action(this.View);
				});
		}

		#endregion

		#region ## Protected Interface (AccessViewAsync) ##

		/// <summary>
		/// Gives the supplied action access to the view. The supplied action is asynchronously executed in the 
		/// thread that created the associated view. The method returns immediately.
		/// </summary>
		/// 
		/// <param name="action">
		/// The action that gains access to the view.</param>
		/// 
		/// <returns>
		/// A task that represents the asynchronously running action. This task can be used to wait for completion, 
		/// to register a continuation and to access the state of the action execution.</returns>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected Task AccessViewAsync(Action<IView> action)
		{
			Verify.NotNull(action, "action");

			var task = mView.InvokeAsync(() =>
				{
					try
					{
						// call callback
						action(this.View);
					}
					catch (Exception exception)
					{
						#region (tracing)
						{
							Trace.WriteLine(String.Format(CultureInfo.InvariantCulture,
								"*** ERROR: action '{0}' supplied to AccessViewAsync() threw an exception:\r\n\r\n{1}",
								action,
								Helper.FormatException(exception, CultureInfo.InvariantCulture)));
						}
						#endregion

						throw;
					}
				});

			return task.IgnoreExceptions();
		}

		#endregion

		#region ## Protected Interface (RunViewAsync) ##

		/// <summary>
		/// Runs the supplied action asynchronously on a background thread.
		/// 
		/// Before running the actual action a prepare action is run. This action is called in the context of the
		/// user interface thread and provides direct access to the view.
		/// 
		/// After completion or failure either the success action or the failure action is called. Both actions 
		/// will be called in the context of the user interface thread and provide direct access to the view.
		/// </summary>
		/// 
		/// <param name="prepareAction">
		/// The action is run before the actual action. This action is called in the context of the user interface 
		/// thread and provides immediate access to the view.</param>
		/// <param name="asyncAction">
		/// The action to run asynchronously on a background thread.</param>
		/// <param name="successAction">
		/// The action that is run in case of successful completion. This action is called in the context of the
		/// user interface thread and provides immediate access to the view.</param>
		/// <param name="failureAction">
		/// The action that is run in case of a failure. This action is called in the context of the user interface 
		/// thread and provides immediate access to the view and the exception that caused the failure.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected Task<TResult> RunViewAsync<TResult>(
			Action<IView> prepareAction,
			Func<TResult> asyncAction,
			Action<IView, TResult> successAction,
			Action<IView, Exception> failureAction)
		{
			Verify.NotNull(prepareAction, "prepareAction");
			Verify.NotNull(asyncAction, "action");
			Verify.NotNull(successAction, "successAction");
			Verify.NotNull(failureAction, "failureAction");

			try
			{
				// preparation: run in user interface thread
				this.AccessView(view =>
					{
						prepareAction(view);
					});
			}
			catch (Exception prepareActionException)
			{
				// return an already-completed-but-failed task
				var taskSource = new TaskCompletionSource<TResult>();
				taskSource.SetException(prepareActionException);
				return taskSource.Task.IgnoreExceptions();
			}

			var task = Task.Factory.StartNew<TResult>(() =>
			{
				try
				{
					// run asynchronously
					var result = asyncAction();

					try
					{
						// success: run in user interface thread
						this.AccessView(view =>
							{
								successAction(view, result);
							});
					}
					catch (Exception successActionException)
					{
						#region (tracing)
						{
							Trace.WriteLine(String.Format(CultureInfo.InvariantCulture,
								"*** ERROR: Success action '{0}' supplied to RunViewAsync() threw an exception:\r\n\r\n{1}",
								successAction,
								Helper.FormatException(successActionException, CultureInfo.InvariantCulture)));
						}
						#endregion

						// ignore
					}

					// forward result value to returned task
					return result;
				}
				catch (Exception actionException)
				{
					try
					{
						// failure: run in user interface thread
						this.AccessView(view =>
							{
								failureAction(view, actionException);
							});
					}
					catch (Exception failureActionException)
					{
						#region (tracing)
						{
							Trace.WriteLine(String.Format(CultureInfo.InvariantCulture,
								"*** ERROR: Failure action '{0}' supplied to RunViewAsync() threw an exception:\r\n\r\n{1}",
								successAction,
								Helper.FormatException(failureActionException, CultureInfo.InvariantCulture)));
						}
						#endregion

						// ignore
					}

					// forward exception to returned task
					throw;
				}
			});

			return task.IgnoreExceptions();
		}

		#endregion

		#region ## Protected Interface (ShowWindow) ##

		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		/// 
		/// <param name="windowDescription">
		/// The window description that specifies which window should be shown.</param>
		/// <param name="environment">
		/// The agent environment that should be forwarded to the window's controller.</param>
		/// <param name="options">
		/// The agent options that should be forwarded to the window's controller.</param>
		/// 
		/// <returns>
		/// The method returns the <see cref="ICompletable"/> reference of the window description, which 
		/// can be used to tear down the window and associated controller.</returns>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected ICompletable ShowWindow(WindowDescription windowDescription, AgentEnvironment environment,
			AgentOptions options)
		{
			Verify.NotNull(windowDescription, "windowDescription");
			Verify.NotNull(environment, "environment");
			Verify.NotNull(options, "options");

			this.Send(new ShowWindowAction(
				windowDescription,
				environment,
				options));

			return windowDescription;
		}

		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		/// 
		/// <param name="windowDescription">
		/// The window description that specifies which window should be shown.</param>
		/// <param name="options">
		/// The agent options that should be forwarded to the window's controller.</param>
		/// 
		/// <returns>
		/// The method returns the <see cref="ICompletable"/> reference of the window description, which 
		/// can be used to tear down the window and associated controller.</returns>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected ICompletable ShowWindow(WindowDescription windowDescription, AgentOptions options)
		{
			return this.ShowWindow(
				windowDescription,
				base.Environment,
				options);
		}

		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		/// 
		/// <returns>
		/// The method returns the <see cref="ICompletable"/> reference of the window description, which 
		/// can be used to tear down the window and associated controller.</returns>
		protected ICompletable ShowWindow(WindowDescription windowDescription)
		{
			return this.ShowWindow(
				windowDescription,
				base.Environment,
				AgentOptions.Empty);
		}


		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		/// 
		/// <param name="environment">
		/// The agent environment that should be forwarded to the window's controller.</param>
		/// <param name="options">
		/// The agent options that should be forwarded to the window's controller.</param>
		/// 
		/// <returns>
		/// The method returns the <see cref="ICompletable"/> reference of the window description, which 
		/// can be used to tear down the window and associated controller.</returns>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected ICompletable ShowWindow<TWindowDescription>(AgentEnvironment environment, AgentOptions options)
			where TWindowDescription : WindowDescription, new()
		{
			return this.ShowWindow(
				new TWindowDescription(),
				environment,
				options);
		}

		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		/// 
		/// <param name="options">
		/// The agent options that should be forwarded to the window's controller.</param>
		/// 
		/// <returns>
		/// The method returns the <see cref="ICompletable"/> reference of the window description, which 
		/// can be used to tear down the window and associated controller.</returns>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected ICompletable ShowWindow<TWindowDescription>(AgentOptions options)
			where TWindowDescription : WindowDescription, new()
		{
			return this.ShowWindow(
				new TWindowDescription(),
				base.Environment,
				options);
		}

		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		/// 
		/// <returns>
		/// The method returns the <see cref="ICompletable"/> reference of the window description, which 
		/// can be used to tear down the window and associated controller.</returns>
		protected ICompletable ShowWindow<TWindowDescription>()
			where TWindowDescription : WindowDescription, new()
		{
			return this.ShowWindow(
				new TWindowDescription(),
				base.Environment,
				AgentOptions.Empty);
		}

		#endregion

		#region ## Protected Interface (ShowDialog) ##

		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		/// 
		/// <param name="windowDescription">
		/// The window description that specifies which window should be shown.</param>
		/// <param name="environment">
		/// The agent environment that should be forwarded to the window's controller.</param>
		/// <param name="options">
		/// The agent options that should be forwarded to the window's controller.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected Nullable<Boolean> ShowDialog(WindowDescription windowDescription, AgentEnvironment environment,
			AgentOptions options)
		{
			Verify.NotNull(windowDescription, "windowDescription");
			Verify.NotNull(environment, "environment");
			Verify.NotNull(options, "options");

			using (windowDescription)
			{
				var action = new ShowDialogAction(
					windowDescription,
					environment,
					options);

				this.Send(action);

				return action.DialogResult;
			}
		}

		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		/// 
		/// <param name="windowDescription">
		/// The window description that specifies which window should be shown.</param>
		/// <param name="options">
		/// The agent options that should be forwarded to the window's controller.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected Nullable<Boolean> ShowDialog(WindowDescription windowDescription, AgentOptions options)
		{
			return this.ShowDialog(
				windowDescription,
				base.Environment,
				options);
		}

		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		protected Nullable<Boolean> ShowDialog(WindowDescription windowDescription)
		{
			return this.ShowDialog(
				windowDescription,
				base.Environment,
				AgentOptions.Empty);
		}


		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		/// 
		/// <param name="environment">
		/// The agent environment that should be forwarded to the window's controller.</param>
		/// <param name="options">
		/// The agent options that should be forwarded to the window's controller.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected Nullable<Boolean> ShowDialog<TWindowDescription>(AgentEnvironment environment, AgentOptions options)
			where TWindowDescription : WindowDescription, new()
		{
			return this.ShowDialog(
				new TWindowDescription(),
				environment,
				options);
		}

		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		/// 
		/// <param name="options">
		/// The agent options that should be forwarded to the window's controller.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected Nullable<Boolean> ShowDialog<TWindowDescription>(AgentOptions options)
			where TWindowDescription : WindowDescription, new()
		{
			return this.ShowDialog(
				new TWindowDescription(),
				base.Environment,
				options);
		}

		/// <summary>
		/// Shows a non-modal window specified by the supplied window description.
		/// </summary>
		protected Nullable<Boolean> ShowDialog<TWindowDescription>()
			where TWindowDescription : WindowDescription, new()
		{
			return this.ShowDialog(
				new TWindowDescription(),
				base.Environment,
				AgentOptions.Empty);
		}

		#endregion

		#region ## Protected Interface (CloseWindow) ##

		/// <summary>
		/// Sends a request to close the window to the view associated with this controller.
		/// </summary>
		protected void CloseWindow()
		{
			this.Send(new CloseWindowAction());
		}

		/// <summary>
		/// Sends a request to close the window with the specified dialog result to the view 
		/// associated with this controller.
		/// </summary>
		/// 
		/// <param name="dialogResult">
		/// The dialog result with which the window should close.</param>
		protected void CloseWindow(Boolean dialogResult)
		{
			this.Send(new CloseWindowAction(dialogResult));
		}

		#endregion

		#region ## Protected Interface (Message Boxes) ##

		/// <summary>
		/// Displays an information message box in front of this window.
		/// </summary>
		/// 
		/// <param name="message">
		/// A string that specifies the text to display.</param>
		/// <param name="caption">
		/// A string that specifies the title bar caption to display.</param>
		/// <param name="arguments">
		/// An optional list of arguments formatted into the message and caption.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		/// <exception cref="ArgumentException">
		/// An empty string was passed to a method that did not accept it as a valid argument.</exception>
		protected void ShowInformationMessageBox(String message, String caption, params Object[] arguments)
		{
			this.Send(new ShowInformationMessageBoxAction(
				message,
				caption,
				arguments));
		}

		/// <summary>
		/// Displays a warning message box in front of this window.
		/// </summary>
		/// 
		/// <param name="message">
		/// A string that specifies the text to display.</param>
		/// <param name="caption">
		/// A string that specifies the title bar caption to display.</param>
		/// <param name="arguments">
		/// An optional list of arguments formatted into the message and caption.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		/// <exception cref="ArgumentException">
		/// An empty string was passed to a method that did not accept it as a valid argument.</exception>
		protected void ShowWarningMessageBox(String message, String caption, params Object[] arguments)
		{
			this.Send(new ShowWarningMessageBoxAction(
				message,
				caption,
				arguments));
		}

		/// <summary>
		/// Displays an error message box in front of this window.
		/// </summary>
		/// 
		/// <param name="message">
		/// A string that specifies the text to display.</param>
		/// <param name="caption">
		/// A string that specifies the title bar caption to display.</param>
		/// <param name="arguments">
		/// An optional list of arguments formatted into the message and caption.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		/// <exception cref="ArgumentException">
		/// An empty string was passed to a method that did not accept it as a valid argument.</exception>
		protected void ShowErrorMessageBox(String message, String caption, params Object[] arguments)
		{
			this.Send(new ShowErrorMessageBoxAction(
				message,
				caption,
				arguments));
		}

		/// <summary>
		/// Displays a confirmation (yes, no) message box in front of this window.
		/// </summary>
		/// 
		/// <param name="message">
		/// A string that specifies the text to display.</param>
		/// <param name="caption">
		/// A string that specifies the title bar caption to display.</param>
		/// <param name="arguments">
		/// An optional list of arguments formatted into the message and caption.</param>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		/// <exception cref="ArgumentException">
		/// An empty string was passed to a method that did not accept it as a valid argument.</exception>
		protected Boolean ShowConfirmationMessageBox(String message, String caption, params Object[] arguments)
		{
			var action = new ShowConfirmationMessageBoxAction(
				message,
				caption,
				arguments);

			this.Send(action);

			return action.Confirmed;
		}

		#endregion

		#region ## Protected Interface (Commands) ##

		/// <summary>
		/// Returns a newly instantiated <see cref="ViewCommand"/> that delegates execution to the specified 
		/// callback. This new command instance will be automatically disposed when the controller is completed,
		/// thus, preventing that the callback will be called after the controller has been completed.
		/// </summary>
		/// 
		/// <param name="executeCallback">
		/// A callback method called when executing the command.</param>
		/// 
		/// <returns>
		/// A reference to the newly instantiated command.</returns>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected ViewCommand CreateCommand(Action executeCallback)
		{
			Verify.NotNull(executeCallback, "executeCallback");

			var command = new ViewCommand(
				executeCallback);

			_RegisterCommand(command);

			return command;
		}

		/// <summary>
		/// Returns a newly instantiated <see cref="ViewCommand"/> that delegates execution to the specified 
		/// callbacks. This new command instance will be automatically disposed when the controller is completed,
		/// thus, preventing that the callbacks will be called after the controller has been completed.
		/// </summary>
		/// 
		/// <param name="executeCallback">
		/// A callback method called when executing the command.</param>
		/// <param name="canExecuteCallback">
		/// A callback method called to determine whether the command can be executed.</param>
		/// 
		/// <returns>
		/// A reference to the newly instantiated command.</returns>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected ViewCommand CreateCommand(Action executeCallback, Func<Boolean> canExecuteCallback)
		{
			Verify.NotNull(executeCallback, "executeCallback");
			Verify.NotNull(canExecuteCallback, "canExecuteCallback");

			var command = new ViewCommand(
				executeCallback,
				canExecuteCallback);

			_RegisterCommand(command);

			return command;
		}


		/// <summary>
		/// Returns a newly instantiated <see cref="ViewCommand"/> that delegates execution to the specified 
		/// callback. This new command instance will be automatically disposed when the controller is completed,
		/// thus, preventing that the callback will be called after the controller has been completed.
		/// </summary>
		/// 
		/// <param name="executeCallback">
		/// A callback method called when executing the command.</param>
		/// <param name="ignoreNullParameters">
		/// A boolean value indicating whether calls with null parameter should be ignored.</param>
		/// 
		/// <returns>
		/// A reference to the newly instantiated command.</returns>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected ViewCommand<T> CreateCommand<T>(Action<T> executeCallback, Boolean ignoreNullParameters = true)
		{
			Verify.NotNull(executeCallback, "executeCallback");

			var command = new ViewCommand<T>(
				executeCallback,
				null,
				ignoreNullParameters);

			_RegisterCommand(command);

			return command;
		}

		/// <summary>
		/// Returns a newly instantiated <see cref="ViewCommand"/> that delegates execution to the specified 
		/// callbacks. This new command instance will be automatically disposed when the controller is completed,
		/// thus, preventing that the callbacks will be called after the controller has been completed.
		/// </summary>
		/// 
		/// <param name="executeCallback">
		/// A callback method called when executing the command.</param>
		/// <param name="canExecuteCallback">
		/// A callback method called to determine whether the command can be executed.</param>
		/// <param name="ignoreNullParameters">
		/// A boolean value indicating whether calls with null parameter should be ignored.</param>
		/// 
		/// <returns>
		/// A reference to the newly instantiated command.</returns>
		/// 
		/// <exception cref="ArgumentNullException">
		/// A null reference was passed to a method that did not accept it as a valid argument.</exception>
		protected ViewCommand<T> CreateCommand<T>(Action<T> executeCallback, Func<T, Boolean> canExecuteCallback,
			Boolean ignoreNullParameters = true)
		{
			Verify.NotNull(executeCallback, "executeCallback");
			Verify.NotNull(canExecuteCallback, "canExecuteCallback");

			var command = new ViewCommand<T>(
				executeCallback,
				canExecuteCallback,
				ignoreNullParameters);

			_RegisterCommand(command);

			return command;
		}

		#endregion

		#region ## Overridable Methods ##

		/// <summary>
		/// This method is called once during construction to give derived types a chance to pre-initialize the 
		/// view model before it is assigned to the view. Override this method to initialize the given view model
		/// to its initial state, e.g. instantiate and assign commands.
		/// 
		/// Note that there is no need to raise property changed notifications, because the view model is not 
		/// assigned to the view yet.
		/// 
		/// Warning: This method is called during construction of the controller, meaning it is called from the 
		/// base class's constructor, thus, it is possible that this method is called before your own constructor. 
		/// Therefore, it is recommended to use this method just to initialize the given view model instance. Don't
		/// try to post or publish messages, don't access local member fields, and so forth.
		/// 
		/// This method is called in the UI dispatcher thread.
		/// </summary>
		/// 
		/// <param name="viewModel">
		/// A reference to the view model instance that should be initialized.</param>
		protected virtual void OnPresetViewModel(Object viewModel)
		{
			// default implementation
		}

		#endregion

		#region ~~ Internal Interface ~~

		/// <summary>
		/// </summary>
		internal IView View
		{
			get
			{
				return mView;
			}
		}

		/// <summary>
		/// </summary>
		internal Object ViewModel
		{
			get
			{
				return mView.ViewModel;
			}
		}

		#endregion

		#region Implementation

		private void _RegisterCommand(IDisposable command)
		{
			mCommands.Add(command);
		}


		/// <summary>
		/// This method supports the agent infrastructure and is not intended to be used directly.
		/// </summary>
		protected override Task OnComplete(Boolean discardAvailableMessages)
		{
			// dispose commands
			foreach (var command in mCommands.ToArray())
				command.Dispose();
			mCommands.Clear();

			return base.OnComplete(discardAvailableMessages);
		}

		#endregion

	}
}
